// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	9/16/2014 5:33:22 PM				
// -----------------------------

#include "Log.h"
#include "Common.h"

#include <time.h>
#include <varargs.h>
#include <algorithm>

#include "Thread.h"
#include "Mutex.h"

TString FormatNumber(int number, int prec)
{
	TStringStream stream;
	for (int i = 0; i < (prec - 1); ++i)
	{
		int maxValue = 10 * (prec - 1);
		if (abs(number) < maxValue)
			stream << _T("0");
	}
	stream << number;
	return stream.str();
}
TString GetTimeStamp()
{
	time_t thisTime;
	time(&thisTime);
	tm t = {};
	localtime_s(&t, &thisTime);

	TStringStream stream;
	stream << FormatNumber(t.tm_hour, 2) << _T(":") << FormatNumber(t.tm_min, 2) << _T(":") << FormatNumber(t.tm_sec, 2);

	return stream.str();
}
void FlushThreadProc(Thread* lpThread, void* lpArgs)
{
	while (true)
	{
		// Flush the Log
		Log::Flush();
		// Sleep for 3 seconds
		Sleep(3000);
	}
}

TOutputStream Log::s_Stream;
Thread* Log::s_lpFlushThread = nullptr;
Mutex* Log::s_lpMutex = nullptr;

void Log::Open(const TChar* strFile)
{
	s_Stream.open(strFile);
	if (s_lpFlushThread)
	{
		s_lpFlushThread->Stop();
		delete s_lpFlushThread;
	}
	if (s_lpMutex)
	{
		s_lpMutex->Leave();
		delete s_lpMutex;
	}
	s_lpFlushThread = new Thread();
	s_lpFlushThread->Start(FlushThreadProc, nullptr);

	s_lpMutex = new Mutex();
	s_lpMutex->Init(_T("LogMutex"));
}
void Log::Flush()
{
	if (Log::TryEnter())
	{
		if (s_Stream.is_open())
			s_Stream.flush();

		Log::Leave();
	}
}
void Log::Close()
{
	s_Stream.close();
	if (s_lpFlushThread)
	{
		s_lpFlushThread->Stop();
		delete s_lpFlushThread;
		s_lpFlushThread = nullptr;
		delete s_lpMutex;
		s_lpMutex = nullptr;
	}
}

void Log::Write(const TChar* lpLine)
{
	if (Log::TryEnter())
	{

		TStringStream stream;
		stream << _T("\n") << _T("[") << GetTimeStamp() << _T("]\t");
		TString timeStamp = stream.str();
		TString strPattern = _T("\n");

		TString strOldLine = lpLine;
		TString strLine = TString(strOldLine.begin(), strOldLine.end() - 1);
		size_t foundIndex = 0;
		size_t offset = 0;
		while (foundIndex != TString::npos)
		{
			foundIndex = strLine.find(_T("\n"), offset);
			if (foundIndex != TString::npos)
			{
				strLine.replace(foundIndex, 1, timeStamp);
				offset += foundIndex + timeStamp.length();
			}
		}
		strLine.append(&strOldLine[strOldLine.length() - 1]);

		if (s_Stream.is_open())
			s_Stream << strLine.c_str();
		OutputDebugString(strLine.c_str());

		Log::Leave();
	}
}
void Log::WriteLine(const TChar* fmt, ...)
{
	TStringStream stream;
	stream << _T("[") << GetTimeStamp() << _T("]\t");

	const size_t maxLen = 1024;
	TChar buffer[maxLen];

	va_list args = nullptr;
	va_start(args, fmt);

	_vstprintf_s(buffer, maxLen, fmt, args);

	va_end(args);

	stream << buffer << "\n";
	Write(stream.str().c_str());
}
void Log::WriteLineSeperator()
{
	if (Log::TryEnter())
	{
		TStringStream stream;
		stream << "[" << GetTimeStamp() << "]\t\n";

		if (s_Stream.is_open())
			s_Stream << stream.str().c_str();
		OutputDebugString(stream.str().c_str());

		Log::Leave();
	}
}
void Log::WriteDashedLine()
{
	TStringStream stream;
	for (int i = 0; i < 128; ++i)
		stream << "-";
	Log::WriteLine(stream.str().c_str());
}

Bool Log::TryEnter()
{
	return s_lpMutex->TryEnter(-1);
}
void Log::Leave()
{
	s_lpMutex->Leave();
}